use std::fs::File;
use std::io;
use std::io::Write;
use std::io::Read;
use std::{process, io::{BufReader}, path::Path};
use crate::{interpreter::Interpreter, parser::Parser, scanner::Scanner};
use crate::expr::EvaluatedValue;
use crate::interpreter::{ExecErr, ExecResult};


pub struct Lox {
    interpreter: Interpreter,
}

impl Lox {
    pub fn new() -> Self {

        Self { interpreter: Interpreter::new() }
    }
    pub fn run_file(&mut self, file: &Path) {
        let mut reader = BufReader::new(File::open(file).expect("open file fail"));
        let mut line = String::new();
        match reader.read_to_string(&mut line) {
            Ok(n) => {
                if n != 0 {
                    match self.run(&line) {
                        Ok(_) => {}
                        Err(e) => {
                            println!("Error: {:?}", e);
                            process::exit(65);
                        }
                    }
                } else {
                    println!("Empty file");
                }
            }
            Err(e) => println!("Load file error: {}", e.to_string())
        }
    }

    pub fn run_prompt(&mut self) {
        loop {
            let mut line = String::new();
            io::stdout().write(b"> ").unwrap();
            io::stdout().flush().unwrap();
            match io::stdin().read_line(&mut line) {
                Ok(n) => {
                    if n == 0 { break }
                    match self.run(&line) {
                        Ok(_) => {},
                        Err(e) => println!("Error: {:?}", e)
                    }
                }
                Err(e) => println!("Error: {}", e.to_string())
            }
        }
    }

    pub fn run(&mut self, line: &str) -> ExecResult {

        let mut scanner = Scanner::new(line);

        let tokens = scanner.scan_tokens().map_err(|e| ExecErr::Other(e))?;

        let mut parser = Parser::new(tokens);

        let statements = parser.parse().map_err(|e| ExecErr::Other(e))?;

        self.interpreter.interpret(&statements)?;

        Ok(EvaluatedValue::nil())
    }
}
